
import os , stat, types, sys
import logging
log = logging.getLogger(__name__)

from twisted.python.rebuild import rebuild
from twisted.internet.task import LoopingCall

class ReloadableHandlers(object):
      reloaders = {}
      looper = None

            
      @classmethod
      def _getSourceFile(cls, obj):
            #TODO the inspect library seems hardset on giving the file of the base class, even if the boundMethod was inherited..
            #Possibly fixed with this
                  
            fname =  obj.__file__
            if fname[-1] == "c": fname = fname[:-1]
            
            return fname
            
                        
      @classmethod
      def reloader(cls, module):
            """
                  Given a module instance, add it to the watch list and next check interval, reload if its
            """
            
            if type(module) == types.StringType:
                  module = sys.modules[module]
                              
            if type(module) != types.ModuleType:
                  raise ValueError("Must supply a valid ModuleType")
                  
            if module in cls.reloaders:
                  #Skip
                  return
            
            fname = cls._getSourceFile(module)
            mtime = os.stat(fname)[stat.ST_MTIME]            
            
            log.info("Adding a reloadable handler %s:%s:%s" % (module, fname, mtime,) )            
            cls.reloaders[module] = (fname, mtime,)

            if cls.looper is None:
                  cls.looper = LoopingCall(cls._reloadcheck)
                  cls.looper.start(3) #todo make this interval configurable


      @classmethod        
      def _reloadcheck(cls):
            """
               When called by LoopingCall, roll through registed handlers looking for newly modified classes
               If this works, I will be amazed
            """
            #split up into two parts to prevent changes being made to cls.reloader until AFTER its down being scanned
            toDereg = []
            
            for module, (fname, mtime) in cls.reloaders.iteritems():
                  if mtime != os.stat(fname)[stat.ST_MTIME]:                        
                        toDereg.append(module)                        

            if len(toDereg) == 0: return
            
            log.info("Reloading targets %s" % toDereg)
            
            for module in toDereg:            
                  try:                                                                                          
                        module = rebuild(module)                        
                  except Exception:                        
                        log.exception("Failed reloading %s"  % module)                        
                        pass # Assume the failure was because of a Syntax error
                  else:                        
                        del(cls.reloaders[module])
                        cls.reloader(module)